/**
 * 事物管理器
 * Created by dell2 on 2016/12/7.
 */
var TransactionsDB = require('../../db/transactions'),
    transactions = new TransactionsDB(),
    uuidV4 = require('uuid/v4'); //uuid生成器开源地址 https://github.com/broofa/node-uuid
function TxManager(){
}

//定义所有操作常量
TxManager.prototype.CREATEAPP     = "createApplication" ;
TxManager.prototype.DELETEAPP     = "deleteApplication" ;
TxManager.prototype.UPDATEAPP     = "updateApplication" ;
TxManager.prototype.MODIFYRC      = "modifyRc";
TxManager.prototype.GRAYUPDATE    = "grayudp" ;
TxManager.prototype.CREATECOMP    = "createComp" ;
TxManager.prototype.DELETECOMP    = "deleteComp" ;
TxManager.prototype.UPDATECOMP    = "updateComp" ;
TxManager.prototype.CREATESTORAGE = "createstorage" ;
TxManager.prototype.DELSTORAGE    = "delstorage" ;

TxManager.prototype.begin = function (bid ,op,data) {
   if( !bid || !op ){
       throw "no operation specified" ;
   }

  return begin(bid ,op,data) ;
}
TxManager.prototype.beginRestore = function (bid  ,newbid ) {
    if( !bid || !newbid ){
        throw "no operation specified" ;
    }
    //change the bid of the transaction
    return beginRestore(bid ,newbid ) ;
}

TxManager.prototype.uuid = function () {
    return uuidV4() ;
}

TxManager.prototype.commit = function ( bid ) {
    if( !bid ){
        throw "no transaction specified" ;
    }
    return commit( bid ) ;
}
//@Deprecated
TxManager.prototype.startRollback = function ( bid ) {
    if( !bid ){
        throw "no transaction specified" ;
    }
    return startRollBack( bid ) ;
}

TxManager.prototype.rollback = function ( bid ) {
    if( !bid ){
        throw "no transaction specified" ;
    }
   return  rollback( bid ) ;
}

TxManager.prototype.setErrStatus = function ( bid ) {
    if( !bid ){
        throw "no transaction specified" ;
    }
    return TxException( bid ) ;
}

/**
 *  初始化事物记录
     将操作信息写入到transactions表，包含以下字段：
     txid: 事物id （uuid）
     source          包含对应记录的_id值
     operation       操作类型（创建、更新、删除）
     state           表示操作当前状态，state字段可选值范围为initial, pending, applied, done, canceling和 canceled
     lastModified    表示最后更新时间
     将服务创建初始化到transactions集合， state字段值为"initial"， lastModified字段值设为当前时间：
         ...
     db.transactions.insert(
     { txid: 1, source: "A",..., lastModified: new Date() }
     )
 */
function begin(bid ,op,data){
    var tx = {
        //txid: uuidV4(), //useless
        source: bid , //对应具体业务id,unique index
        operation: op ,
        data:data,
        state:"begin",
        lastModified:new Date(),
        expiredAt: null
    } ;

    return new Promise(function( _resolve,_reject ) {
        transactions.insert(null, tx, function (err, rets) {
            if (err) {
                _reject(Error(err.name, err.message));
            } else {
                if (rets) {
                    ensureUnique( {"source":1} ).then(function () {
                        _resolve(Success(true))
                    }).catch(function ( err ) {
                        _reject(Error("DBError", "Can't create data,"+JSON.stringify( err )));
                    }) ; //create unique index
                }
            }
        });
    }) ;
}
function beginRestore(bid,newbid) {
    return update( getQuery(bid),  { source: newbid  }   ) ;
}
function commit(bid) {
    return update( getQuery(bid), getUpdateFields( "committed" )  ) ;
}
//@deprecated
function startRollBack(bid){
    return update( getQuery(bid), getUpdateFields( "rolling" ,false)  ) ;
}

/**
 * rollback the transaction
 */
function rollback(bid){
    return update(  getQuery(bid) , getUpdateFields( "finished")  ) ;
}

function TxException( bid){
    return update(  getQuery(bid) , getUpdateFields( "error")  ) ;
}

function update(query,tx){
    return new Promise(function( resolve,reject ){
        transactions.updateField(null, query, tx, function (err) {
            if (err) {
                reject( Error( "NoDataFoundError",err.message) );
            } else {
                resolve(  Success( true ) );
                ensureTTL( {"expiredAt":1} ) ; //create ttl index for deleting the expired data
            }
        });
    }) ;
}

function ensureTTL(field){
    var ops = {
        expireAfterSeconds:10
    } ;
    return ensureIndex( field ,ops ) ;
}

function ensureUnique(field){
    var ops = {
        unique:true
    } ;
    return ensureIndex( field , ops  ) ;
}

function ensureIndex(field,options) {
    return new Promise(function (resolve,reject) {
        transactions.ensureIndex(null,field,options ,function (err,rets) {
            if( err ){
                reject( err ) ;
            }else{
                resolve( rets ) ;
            }
        }) ;
    })
}

function Error(name,msg){
    return {
        ret: false,
        "name":  name,
        "message": msg,
        error: msg,
        data: msg
    }
}

function Success(data){
    return {
        ret: true,
        error: null ,
        data:  data
    } ;
}

function getQuery(bid) {
    var query = {
        source: bid , //对应具体业务id
    } ;
    return query ;
}

function getUpdateFields(state,finished) {
    if( finished === undefined || finished === null ){
        finished = true ; //default true
    }
    var tx = {
        state: state ,
        lastModified:new Date(),
        expiredAt: finished?new Date():null   //expired after 3600 seconds
    } ;
    return tx ;
}

module.exports = TxManager;


